﻿#include "encryptutils.h"
#include "publicfun/debug.h"
#include "publicfun/zbase64.h"
#include <openssl/err.h>
#include "publicfun/public.h"
#include <openssl/md5.h>
/*
author:
    gxq
update data:
    20160406
ref page:
    [http://www.qmailer.net/archives/216.html]
*/

EncryptUtils::EncryptUtils()
{
}

string EncryptUtils::EncodeRSAKeyFile( const std::string& strPemFileName, const std::string& strData )
{
    if (strPemFileName.empty() || strData.empty())
    {
        assert(false);
        return "";
    }
    FILE* hPubKeyFile = NULL;
    if((hPubKeyFile = fopen(strPemFileName.c_str(), "rb")) == NULL)
    {
        LOGE("publicKey file(%s) open failed\n", strPemFileName.c_str());
        assert(false);
        return "";
    }
    std::string strRet;
    RSA* pRSAPublicKey = RSA_new();
    if(PEM_read_RSA_PUBKEY(hPubKeyFile, &pRSAPublicKey, 0, 0) == NULL)
    {
        assert(false);
        return "";
    }

    int nLen = RSA_size(pRSAPublicKey);
    char* pEncode = new char[nLen + 1];
    int ret = RSA_public_encrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPublicKey, RSA_PKCS1_PADDING);
    if (ret >= 0)
    {
        strRet = std::string(pEncode, ret);
    }
    delete[] pEncode;
    RSA_free(pRSAPublicKey);
    fclose(hPubKeyFile);
    CRYPTO_cleanup_all_ex_data();
    return strRet;
}

std::string EncryptUtils::DecodeRSAKeyFile( const std::string& strPemFileName, const std::string& strData )
{
    if (strPemFileName.empty() || strData.empty())
    {
        assert(false);
        return "";
    }
    FILE* hPriKeyFile = NULL;
    if((hPriKeyFile = fopen(strPemFileName.c_str(),"rb")) == NULL)
    {
        assert(false);
        return "";
    }
    std::string strRet;
    RSA* pRSAPriKey = RSA_new();
    if(PEM_read_RSAPrivateKey(hPriKeyFile, &pRSAPriKey, 0, 0) == NULL)
    {
        assert(false);
        return "";
    }
    int nLen = RSA_size(pRSAPriKey);
    char* pDecode = new char[nLen+1];

    int ret = RSA_private_decrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pDecode, pRSAPriKey, RSA_PKCS1_PADDING);
    if(ret >= 0)
    {
        strRet = std::string((char*)pDecode, ret);
    }
    delete [] pDecode;
    RSA_free(pRSAPriKey);
    fclose(hPriKeyFile);
    CRYPTO_cleanup_all_ex_data();
    return strRet;
}

//pending 0
std::string EncryptUtils::EncodeAES( const std::string& password, const std::string& data )
{
   AES_KEY aes_key;
   if(AES_set_encrypt_key((const unsigned char*)password.c_str(), password.length() * 8, &aes_key) < 0)
   {
       assert(false);
       return "";
   }
   std::string strRet;
   std::string data_bak = data;
   unsigned int data_length = data_bak.length();
   int padding = 0;
   if (data_bak.length() % AES_BLOCK_SIZE > 0)
   {
       padding =  AES_BLOCK_SIZE - data_bak.length() % AES_BLOCK_SIZE;
   }
   data_length += padding;
   while (padding > 0)
   {
       data_bak += '\0';
       padding--;
   }
   for(unsigned int i = 0; i < data_length/AES_BLOCK_SIZE; i++)
   {
       std::string str16 = data_bak.substr(i*AES_BLOCK_SIZE, AES_BLOCK_SIZE);
       unsigned char out[AES_BLOCK_SIZE];
       ::memset(out, 0, AES_BLOCK_SIZE);
       AES_encrypt((const unsigned char*)str16.c_str(), out, &aes_key);
       strRet += std::string((const char*)out, AES_BLOCK_SIZE);
   }
   return strRet;
}

std::string EncryptUtils::DecodeAES( const std::string& strPassword, const std::string& strData )
{
    AES_KEY aes_key;
    if(AES_set_decrypt_key((const unsigned char*)strPassword.c_str(), strPassword.length() * 8, &aes_key) < 0)
    {
        assert(false);
        return "";
    }
    std::string strRet;
    for(unsigned int i = 0; i < strData.length()/AES_BLOCK_SIZE; i++)
    {
        std::string str16 = strData.substr(i*AES_BLOCK_SIZE, AES_BLOCK_SIZE);
        unsigned char out[AES_BLOCK_SIZE];
        ::memset(out, 0, AES_BLOCK_SIZE);
        AES_decrypt((const unsigned char*)str16.c_str(), out, &aes_key);
        strRet += std::string((const char*)out, AES_BLOCK_SIZE);
    }
    return strRet;
}


/*
key:
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024,new SecureRandom());
// 生成一个密钥对，保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
key=publicKey.getEncoded()
//TODO:find better way??
*/
string EncryptUtils::EncodeRSAByKey( const std::string& key, const std::string& strData )
{
    std::string strRet;
    BIO *bio = NULL;
    RSA *pRSAPublicKey = NULL;
    int ret;
    int nLen = 0;
    char* pEncode = NULL;
    string strPublicKey;
    ZBase64::EncodeLB(key.c_str(), key.length(), strPublicKey);
    strPublicKey.insert(0, "-----BEGIN PUBLIC KEY-----\n");
    strPublicKey.append("\n-----END PUBLIC KEY-----\n");

    char *chPublicKey = const_cast<char *>(strPublicKey.c_str());
    if ((bio = BIO_new_mem_buf(chPublicKey, -1)) == NULL){       //从字符串读取RSA公钥
        LOGE("BIO_new_mem_buf failed!\n");
        goto END;
    }
    pRSAPublicKey = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);   //从bio结构中得到rsa结构
    if (!pRSAPublicKey){
        LOGE("bio read key from str failed\n");
        goto END;
    }

    nLen = RSA_size(pRSAPublicKey);
    pEncode = new char[nLen + 1];
    ret = RSA_public_encrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPublicKey, RSA_PKCS1_PADDING);
    if (ret >= 0){
        strRet = std::string(pEncode, ret);
    }
END:
    if(pEncode){
        delete[] pEncode;
    }
    if(bio){
        BIO_free_all(bio);
    }
    if(pRSAPublicKey){
        RSA_free(pRSAPublicKey);
    }
    CRYPTO_cleanup_all_ex_data();
    return strRet;
}

string EncryptUtils::DecodeRSAByKey( const std::string& key, const std::string& strData )
{
    std::string strRet;
    BIO *bio = NULL;
    RSA *rsaPrivateKey = NULL;
    int ret;
    int nLen = 0;
    char* pDecode = NULL;
    string privateKeyStr;
    ZBase64::EncodeLB(key.c_str(), key.length(), privateKeyStr);
    privateKeyStr.insert(0, "-----BEGIN RSA PRIVATE KEY-----\n");
    privateKeyStr.append("\n-----END RSA PRIVATE KEY-----\n");

    char *chPrivateKey = const_cast<char *>(privateKeyStr.c_str());
    if ((bio = BIO_new_mem_buf(chPrivateKey, -1)) == NULL){       //从字符串读取RSA公钥
        LOGE("BIO_new_mem_buf failed!\n");
        goto END;
    }
    rsaPrivateKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
    //rsaPrivateKey = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);   //从bio结构中得到rsa结构
    if (!rsaPrivateKey){
        LOGE("bio read key from str failed\n");
        goto END;
    }

    nLen = RSA_size(rsaPrivateKey);
    pDecode = new char[nLen + 1];
    ret = RSA_private_decrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pDecode, rsaPrivateKey, RSA_PKCS1_PADDING);
    if (ret >= 0){
        strRet = std::string(pDecode, ret);
    }
END:
    if(pDecode){
        delete[] pDecode;
    }
    if(bio){
        BIO_free_all(bio);
    }
    if(rsaPrivateKey){
        RSA_free(rsaPrivateKey);
    }
    CRYPTO_cleanup_all_ex_data();
    return strRet;
}

//cbc7
std::string EncryptUtils::EncodeAESCBC(const std::string& password, const std::string& iv, const std::string& indata)
{
    AES_KEY aes_key;
    size_t padlen = AES_BLOCK_SIZE;
    string ret;
    string data = indata;
#if 0
    d_debug_bin_buffer((const unsigned char *)password.c_str(), password.length());
    d_debug_bin_buffer(iv.c_str(), iv.length());
#endif
    if(AES_set_encrypt_key((const unsigned char*)password.c_str(), password.length() * 8, &aes_key) < 0){
        assert(false);
        return "";
    }

    if (data.length() % AES_BLOCK_SIZE > 0){
        padlen =  AES_BLOCK_SIZE - data.length() % AES_BLOCK_SIZE;
        string temp;
		temp.assign(padlen, padlen);
        data.append(temp);
    }

    ret.resize(data.length());
    unsigned char *retbuf = (unsigned char *)ret.data();
#ifdef __linux__
    unsigned char tempiv[iv.size()];
#else
    unsigned char *tempiv = (unsigned char *)malloc(iv.size());
#endif
    memcpy(tempiv, iv.c_str(), iv.length());
    AES_cbc_encrypt((unsigned char *)data.c_str(), retbuf, data.length(), &aes_key, tempiv, AES_ENCRYPT);
    //d_debug_bin_buffer((const unsigned char *)ret.c_str(), ret.length());
#ifdef __linux__
    
#else
    free(tempiv);
#endif
    return ret;
}

std::string EncryptUtils::DecodeAESCBC( const std::string& strPassword, const std::string& iv, const std::string& strData )
{
    AES_KEY aes_key;

#if 0
    d_debug_bin_buffer((const unsigned char *)strPassword.c_str(), strPassword.length());
    d_debug_bin_buffer(iv.c_str(), iv.length());
#endif
    if(AES_set_decrypt_key((const unsigned char*)strPassword.c_str(), strPassword.length() * 8, &aes_key) < 0){
        assert(false);
        return "";
    }

    string out;
    out.resize(strData.length());
#ifdef __linux__
    unsigned char tempiv[iv.size()];
#else
    unsigned char *tempiv = (unsigned char *)malloc(iv.size());
#endif
    memcpy(tempiv, iv.c_str(), iv.length());

    AES_cbc_encrypt((unsigned char*)strData.c_str(), (unsigned char*)out.data(), strData.length(), &aes_key, tempiv, AES_DECRYPT);

    if(out.c_str()[out.length()-1] != 0){
        out.resize(out.length()-out.c_str()[out.length()-1]);
    }
#ifdef __linux__

#else
    free(tempiv);
#endif

    return out;
}

string sha1sum(string &data)
{
    unsigned char md[SHA_DIGEST_LENGTH];
    SHA1((const unsigned char *)data.c_str(), data.size(), md);
    //LOGBIN(md, SHA_DIGEST_LENGTH);
    return string((char*)md, SHA_DIGEST_LENGTH);
}
#if 0
void myHash224()
{
    SHA256_CTX c;
    unsigned char md[SHA224_DIGEST_LENGTH];
    SHA224((unsigned char *)orgStr, strlen(orgStr), md);
    printHash(md, SHA224_DIGEST_LENGTH);

    SHA224_Init(&c);
    SHA224_Update(&c, orgStr, strlen(orgStr));
    SHA224_Final(md, &c);
    OPENSSL_cleanse(&c, sizeof(c));
    printHash(md, SHA224_DIGEST_LENGTH);
}

void myHash256()
{
    SHA256_CTX c;
    unsigned char md[SHA256_DIGEST_LENGTH];
    SHA256((unsigned char *)orgStr, strlen(orgStr), md);
    printHash(md, SHA256_DIGEST_LENGTH);

    SHA256_Init(&c);
    SHA256_Update(&c, orgStr, strlen(orgStr));
    SHA256_Final(md, &c);
    OPENSSL_cleanse(&c, sizeof(c));
    printHash(md, SHA256_DIGEST_LENGTH);
}

void myHash384()
{
    SHA512_CTX c;
    unsigned char md[SHA384_DIGEST_LENGTH];
    SHA384((unsigned char *)orgStr, strlen(orgStr), md);
    printHash(md, SHA384_DIGEST_LENGTH);

    SHA384_Init(&c);
    SHA384_Update(&c, orgStr, strlen(orgStr));
    SHA384_Final(md, &c);
    OPENSSL_cleanse(&c, sizeof(c));
    printHash(md, SHA384_DIGEST_LENGTH);
}

void myHash512()
{
    SHA512_CTX c;
    unsigned char md[SHA512_DIGEST_LENGTH];
    SHA512((unsigned char *)orgStr, strlen(orgStr), md);
    printHash(md, SHA512_DIGEST_LENGTH);

    SHA512_Init(&c);
    SHA512_Update(&c, orgStr, strlen(orgStr));
    SHA512_Final(md, &c);
    OPENSSL_cleanse(&c, sizeof(c));
    printHash(md, SHA512_DIGEST_LENGTH);
}
#endif

//Quoted-Printable也是MIME邮件中常用的编码方式之一。同Base64一样，
//它也将输入的字符串或数据编码成全是ASCII码的可打印字符串。
//Quoted-Printable编码的基本方法是：输入数据在33-60、62-126范围内的，
//直接输出；其它的需编码为“=”加两个字节的HEX码(大写)。
//为保证输出行不超过规定长度，可在行尾加“=/r/n”序列作为软回车。
string Qp_encode(string src)
{
    int line_len;
    int max_line_len=512;    // 输出的行长度计数
    line_len = 0;
    char tmp[12];
    string dst;
    for (size_t i = 0; i < src.size(); i++)
    {
        // ASCII 33-60, 62-126原样输出，其余的需编码
        if ((src[i] >= '!') && (src[i] <= '~') && (src[i] != '='))
        {
            dst+=src[i];
            line_len++;
        }
        else
        {
            char c1 = 0x0F & (src[i] >> 4);
            char c2 = 0x0F & src[i];
            dst += '=';
            dst += (c1 < 0xA) ? (c1 + 48):(c1 + 55);
            dst += (c2 < 0xA) ? (c2 + 48):(c2 + 55);
            line_len += 3;        }
        // 输出换行？
        if (line_len >= max_line_len)
        {
            sprintf(tmp, "=\r\n");
            dst+=tmp;
            line_len = 0;
        }
    }
    dst+='\0';
    // 输出加个结束符
    return dst;
}

string md5sum(const char *path)
{  
    MD5_CTX ctx;
    int len = 0;
    unsigned char buffer[1024] = {0};
    unsigned char digest[16] = {0};

    FILE *pFile = fopen (path, "rb");
    if(pFile == NULL){
        return "";
    }

    MD5_Init (&ctx);
    while ((len = fread (buffer, 1, 1024, pFile)) > 0){  
        MD5_Update (&ctx, buffer, len);  
    }  

    MD5_Final (digest, &ctx);  
    fclose(pFile);  

    return string((char *)digest, 16);  
}  

